﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Windows.Forms;
using IBNet.UtilsDataStructuresAndExtensions;
using System.IO;
using System.Net.Sockets;
using System.Collections.ObjectModel;

namespace IBNet.LongTermTrade
{
   public interface IWrapLongTermTradeMainForm
   {
      /// <summary>
      /// Submits an order to TWS.
      /// </summary>
      /// <param name="order">The order to be submitted to TWS.</param>
      /// <param name="contract">The contract the order is for.</param>
      /// <param name="orderFlavor">The OrderFlavor of the order (Opening, Bracket, or None).</param>
      void SubmitOrder(Order order, Contract contract, OrderFlavor orderFlavor);

      /// <summary>
      /// Requests market data for a contract. Market data is returned via the IBClient events
      /// PriceTickReceived, SizeTickReceived, OptionComputationTickReceived, 
      /// GenericTickReceived, StringTickReceived, and EfpTickReceived.
      /// </summary>
      /// <param name="contract">The contract to request market data for.</param>
      /// <returns>The marketDataRequestId used to request the market data.</returns>
      int RequestMarketData(Contract contract);

      /// <summary>
      /// Connects to TWS.
      /// </summary>
      /// <returns>true if successfully connected to TWS; otherwise, false.</returns>
      bool ConnectToTws();

      /// <summary>
      /// Submit orders to TWS with opening limit prices  
      /// derived from yesterday's closing price (requested from TWS)
      /// for the contracts the order is for.
      /// </summary>
      /// <param name="autoOrderString">
      /// Format "symbol direction symbol direction..."</param>
      void SubmitOrdersWithClosesFromTWS(string tradeRequestsString);
   }

   public partial class LongTermTradeMainForm : Form, IWrapLongTermTradeMainForm
   {
      void IWrapLongTermTradeMainForm.SubmitOrdersWithClosesFromTWS(
         string tradeRequestsString)
      {
         ICollection<TradeRequest> tradeRequests;
         bool didSuccessfullyCreateTradeRequests =
            TryCreateTradeRequestsFromTradeRequestsString(
               tradeRequestsString, out tradeRequests);

         if (didSuccessfullyCreateTradeRequests)
         {
            ICollection<OrderAndContract> ordersAndContractsToBeSubmitted =
               CreateOrdersAndContractsFromTradeRequests(tradeRequests);
            if (ordersAndContractsToBeSubmitted != null)
            {
               SubmitOrders(ordersAndContractsToBeSubmitted);
               textBoxMessages.WriteLine("Done processing order: " + tradeRequestsString);
               textBoxOrderEntry.BeginInvoke((MethodInvoker)delegate
               {
                  textBoxOrderEntry.ResetText();
               });
            }
         }
      }

      private bool TryCreateTradeRequestsFromTradeRequestsString(
         string tradeRequestsString, out ICollection<TradeRequest> tradeRequests)
      {
         string[] tradeRequestsArray = tradeRequestsString.Split(new char[] { ' ' },
            StringSplitOptions.RemoveEmptyEntries);
         if (!TradeRequestsArrayIsValid(tradeRequestsArray))
         {
            tradeRequests = null;
            return false;
         }

         tradeRequests = new List<TradeRequest>(tradeRequestsArray.Length / 2);
         for (int i = 0; i < tradeRequestsArray.Length; i += 2)
         {
            string symbol = tradeRequestsArray[i].ToUpper();
            string tradeDirection = tradeRequestsArray[i + 1].ToUpper();
            ActionSide actionSide = (tradeDirection == "L") ?
               ActionSide.Buy : ActionSide.Sell;
            TradeRequest autoOrder = new TradeRequest(symbol, actionSide);
            tradeRequests.Add(autoOrder);
         }
         return true;
      }

      private bool TradeRequestsArrayIsValid(string[] tradeRequestsArray)
      {
         if (tradeRequestsArray == null || tradeRequestsArray.Length == 0 ||
             tradeRequestsArray.Length % 2 != 0)
         {
            textBoxMessages.WriteLine("Error: Invalid order format.");
            return false;
         }
         
         for (int i = 0; i < tradeRequestsArray.Length; i += 2)
         {
            string actionSide = tradeRequestsArray[i + 1].ToUpper();
            if (actionSide != "L" && actionSide != "S")
            {
               textBoxMessages.WriteLine("Error: Invalid order format.");
               return false;
            }
         }
         return true;
      }

      private ICollection<OrderAndContract> CreateOrdersAndContractsFromTradeRequests(
            ICollection<TradeRequest> tradeRequests)
      {
         List<OrderAndContract> ordersAndContractsToBeSubmitted
            = new List<OrderAndContract>(tradeRequests.Count);
         foreach (TradeRequest tradeRequest in tradeRequests)
         {
            Stock stock = new Stock(tradeRequest.Symbol);
            Order openingOrder = NewOpeningOrder(stock, tradeRequest.ActionSide);
            if (openingOrder != null)
            {
               ordersAndContractsToBeSubmitted.Add(
                  new OrderAndContract(openingOrder, stock));
            }
            else
            {
               textBoxMessages.WriteLine("Error: ", stock.Symbol, " is an invalid symbol.");
               return null;
            }
         }
         return ordersAndContractsToBeSubmitted;
      }

      void IWrapLongTermTradeMainForm.SubmitOrder(Order order, Contract contract, 
         OrderFlavor orderFlavor)
      {
         lock (_onlySubmitStrictlyIncreasingOrderIdsLock)
         {
            order.OrderId = _currentOrderId++;
            OrderAndContract orderAndContract = new OrderAndContract(order, contract);
            if (orderFlavor == OrderFlavor.Opening)
            {
               _openingOrderIds.Add(order.OrderId);
               _orderIdToOrderAndContract.Add(order.OrderId, orderAndContract);
            }            
            client.SubmitOrder(orderAndContract);
         }
      }

      int IWrapLongTermTradeMainForm.RequestMarketData(Contract contract)
      {
         int marketDataRequestId = _currentRequestId++;
         _requestIdForMarketDataToSymbol.Add(marketDataRequestId, contract.Symbol);
         client.RequestMarketData(marketDataRequestId, contract, null, true);
         return marketDataRequestId;
      }

      bool IWrapLongTermTradeMainForm.ConnectToTws()
      {
         if (client.Connected)
         {
            textBoxMessages.WriteLine("Already Connected.");
            return true;
         }
         else if (_areDisconnected)
         {
            textBoxMessages.WriteLine("Cannot reconnect once disconnected.");
            return false;
         }
         try
         {
            // 0 == clientId for DayTrade.exe
            // 1 == clientId for LongTermTrade.exe
            client.ConnectToTws("127.0.0.1", 7496, 1);
            textBoxMessages.WriteLine("Connected at ", DateTime.Now.ToLongTimeString(), ".");
         }
         catch (IOException)
         // For when the user selects "No" at the TWS connection prompt.
         {
            textBoxMessages.WriteLine("Error: Could not connect to TWS.");
            return false;
         }
         catch (SocketException)
         // For when starting the program when not connected to the Internet
         // or starting the program when TWS is not running.
         {
            textBoxMessages.WriteLine("Error: Could not connect to TWS.");
            return false;
         }
         return true;
      }
   }
}
